# -*- coding: utf-8 -*-

import pygame
import game

class EventType(object):
    QUIT = "EVENT_QUIT"
    KEYDOWN = "EVENT_KEYDOWN"
    KEYUP = "EVENT_KEYUP"
    MOUSEMOTION = "EVENT_MOUSEMOTION"
    MOUSEBUTTONUP = "EVENT_MOUSEBUTTONUP"
    MOUSEBUTTONDOWN = "EVENT_MOUSEBUTTONDOWN"

    MOUSE_STATE_DOWN = "EVENT_MOUSE_STATE_DOWN"
    MOUSE_STATE_UP = "EVENT_MOUSE_STATE_DOWN"

    SKILL_CAST = "EVENT_SKILL_CAST"

    PROJECTILE_HIT = "EVENT_PROJECTILE_HIT"
    PROJECTILE_DEATH = "EVENT_PROJECTILE_DEATH"

    UNIT_DAMAGE = "EVENT_UNIT_DAMAGE"
    UNIT_HEAL = "EVENT_UNIT_HEAL"
    UNIT_DEATH = "EVENT_UNIT_DEATH"
    UNIT_LEVEL_UP = "EVENT_UNIT_LEVEL_UP"

class Event(object):

    MOUSE_LEFT = 0
    MOUSE_RIGHT = 2

    #Temporary event variables
    mouse_button = 0
    mouse_position = (0, 0)
    mouse_movement = (0, 0)
    key_code = 0
    key_modifier = 0

    def __init__(self):
        self.trigger_list = {}

    def register_trigger(self, trigger, event_type):
        if event_type not in self.trigger_list:
            self.trigger_list[event_type] = []

        self.trigger_list[event_type].append(trigger)


    def remove_trigger(self, trigger, event_type):
        trigger_list = self.trigger_list[event_type]

        if trigger in trigger_list:
            trigger_list.remove(trigger)

    def remove_trigger_all(self, trigger):
        for key in self.trigger_list.keys():
            self.remove_trigger(trigger)

    def handle(self, event_type):
        if event_type not in self.trigger_list:
            return

        #print "%s triggered" % (event_type)
        trigger_list = self.trigger_list[event_type]

        for trigger in trigger_list:
            if trigger.condition():
                trigger.action()


    #wrapper for pygame event
    def handle_pygame_event(self):
        event_type = None

        mousedown = pygame.mouse.get_pressed()
        keydown = pygame.key.get_pressed()

        if mousedown[Event.MOUSE_LEFT]:
            Event.mouse_position = pygame.mouse.get_pos()
            Event.mouse_button = Event.MOUSE_LEFT
            self.handle(EventType.MOUSE_STATE_DOWN)
        if mousedown[Event.MOUSE_RIGHT]:
            Event.mouse_position = pygame.mouse.get_pos()
            Event.mouse_button = Event.MOUSE_RIGHT
            self.handle(EventType.MOUSE_STATE_DOWN)


        for pygame_event in pygame.event.get():
            if pygame_event.type == pygame.QUIT:
                event_type = EventType.QUIT

            elif pygame_event.type == pygame.KEYDOWN:
                event_type = EventType.KEYDOWN
                Event.key_code = pygame_event.key
                Event.key_modifier = pygame_event.mod

            elif pygame_event.type == pygame.KEYUP:
                event_type = EventType.KEYUP
                Event.key_code = pygame_event.key
                Event.key_modifier = pygame_event.mod

            elif pygame_event.type == pygame.MOUSEMOTION:
                event_type = EventType.MOUSEMOTION
                Event.mouse_movement = pygame_event.rel
                Event.mouse_position = pygame_event.pos

            elif pygame_event.type == pygame.MOUSEBUTTONUP:
                event_type = EventType.MOUSEBUTTONUP
                Event.mouse_position = pygame_event.pos
                Event.mouse_button = pygame_event.button

            elif pygame_event.type == pygame.MOUSEBUTTONDOWN:
                event_type = EventType.MOUSEBUTTONDOWN
                Event.mouse_position = pygame_event.pos
                Event.mouse_button = pygame_event.button


            """
                ACTIVEEVENT	     gain, state
                KEYDOWN	     unicode, key, mod
                KEYUP	     key, mod
                MOUSEMOTION	     pos, rel, buttons
                MOUSEBUTTONUP    pos, button
                MOUSEBUTTONDOWN  pos, button
                JOYAXISMOTION    joy, axis, value
                JOYBALLMOTION    joy, ball, rel
                JOYHATMOTION     joy, hat, value
                JOYBUTTONUP      joy, button
                JOYBUTTONDOWN    joy, button
                VIDEORESIZE      size, w, h
                VIDEOEXPOSE      none
                USEREVENT        code
            """

            """
            elif pygame_event.type == pygame.KEYDOWN:
                event_type = Event.KEYDOWN
            elif pygame_event.type == pygame.KEYDOWN:
                event_type = Event.KEYDOWN
            elif pygame_event.type == pygame.KEYDOWN:
                event_type = Event.KEYDOWN
            elif pygame_event.type == pygame.KEYDOWN:
                event_type = Event.KEYDOWN
            """

            self.handle(event_type)

class Trigger(object):
    #World Level variables
    monster_list = None

    def __init__(self, event):
        self.event = event

    def condition(self):
        return True

    def action(self):
        pass

# TODO: Currently this is a  "super key up event handler"
# Should seperate out the key handler
class TriggerKeyUp(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.KEYUP)

    def action(self):
        if Event.key_code == pygame.K_ESCAPE:
            self.event.handle(EventType.QUIT)
        elif Event.key_code == pygame.K_F1:
            game.Map.game_map.render_option.debug = (
                not game.Map.game_map.render_option.debug
            )
        elif Event.key_code == pygame.K_F3:
            for u in game.Map.all_unit:
                u.render_option.debug = True
        elif Event.key_code == pygame.K_F4:
            for u in game.Map.all_unit:
                u.render_option.debug = False
        else:
            return False

class TriggerQuitGame(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.QUIT)

    def action(self):
        game.Map.run = False


class TriggerLeftMouseStateDown(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.MOUSE_STATE_DOWN)

    def condition(self):
        if Event.mouse_button is Event.MOUSE_LEFT:
            return True

        else:
            return False

    def action(self):
        player = game.Map.player
        mouse_position = game.Map.game_map.get_pos_from_mouse()

        for monster in game.Map.monster_list:
            if monster.get_pos_rect().collidepoint(mouse_position):
                player.target = monster
            else:
                player.target = None

        player.find_path(mouse_position)


class TriggerRightMouseStateDown(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.MOUSE_STATE_DOWN)

    def condition(self):
        if Event.mouse_button is Event.MOUSE_RIGHT:
            return True

        else:
            return False

    def action(self):
        return False


class TriggerUnitDamage(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.UNIT_DAMAGE)

    def condition(self):
        return True

    def action(self):
        target = self.event.damage_target
        source = self.event.damage_source
        amount = self.event.damage_amount

        target.hp -= amount
        target.floating_text.append(game.FloatingText(str(int(amount)), target.position))
        #TODO: target.set_animation(game.Unit.ANIM_HIT)

        if target.hp <= 0:
            self.event.dying_unit = target
            self.event.killing_unit = source
            self.event.handle(EventType.UNIT_DEATH)

class TriggerUnitHeal(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.UNIT_HEAL)

    def condition(self):
        return True

    def action(self):
        target = self.event.heal_target
        source = self.event.heal_source
        amount = self.event.heal_amount

        lost_hp = target.max_hp - target.hp
        if amount > lost_hp:
            amount = lost_hp
            target.hp = target.max_hp

        else:
            target.hp += amount

        floating_text = game.FloatingText(str(int(amount)), target.position)
        floating_text.set_color(pygame.Color(0, 255, 0))

        target.floating_text.append(floating_text)
        #TODO: target.set_animation(game.Unit.ANIM_HIT)

class TriggerUnitDeath(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.UNIT_DEATH)

    def condition(self):
        return True

    def action(self):
        dying_unit = self.event.dying_unit
        killing_unit = self.event.killing_unit

        dying_unit.death(killing_unit)

class TriggerSkillCasted(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.SKILL_CAST)

    def condition(self):
        return True

    def action(self):
        source = self.event.skill_source
        target = self.event.skill_target

        self.event.skill_casted.cast(source, target)

class TriggerProjectileHit(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.PROJECTILE_HIT)

    def condition(self):
        if self.event.projectile.owner.team is self.event.projectile_target.team:
            return False

        if not self.event.projectile_target.alive:
            return False

        if not self.event.projectile.alive:
            return False

        if self.event.projectile_target in self.event.projectile.hitten_list:
            return False

        return True

    def action(self):
        projectile = self.event.projectile
        target = self.event.projectile_target

        projectile.owner.damage(target, projectile.damage)
        projectile.hitten_list.append(target)
        if projectile.remove_on_hit:
            projectile.alive = False

class TriggerProjectileDeath(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.PROJECTILE_DEATH)

    def condition(self):
        return True

    def actino(self):
        pass

class TriggerFireballExplode(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.PROJECTILE_DEATH)

    def condition(self):
        if not isinstance(self.event.projectile.owner, game.Fireball):
            return False

        return True

    def action(self):
        projectile = self.event.projectile
        fireball = projectile.owner

        victim_list = game.get_unit_list_in_range(game.Map.all_unit,
                                                  projectile.position,
                                                  fireball.radius)

        for u in victim_list:
            #if u.alive and u.team is not fireball.owner.team:
            if u.alive:
                fireball.owner.damage(u, fireball.value)

            #check for victim if they still alive, apply DoT if yes
            if u.alive:
                debuff = game.BuffDamageOverTime()
                debuff.owner = fireball.owner
                debuff.target = u
                game.Map.buff_list.append(debuff)

        effect = game.SfxFireball()
        effect.position = projectile.position
        game.Map.effect_list.append(effect)



class TriggerChainLightningHit(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.PROJECTILE_HIT)

    def condition(self):

        if not isinstance(self.event.projectile, game.Chain):
            return False

        if self.event.projectile.owner.team is self.event.projectile_target.team:
            return False

        if not self.event.projectile_target.alive:
            return False

        return True

    def action(self):
        projectile = self.event.projectile
        target = self.event.projectile_target

        if projectile.jump > 0:
            projectile.jump -= 1
            projectile.alive = True
            projectile.set_range(projectile.jump_distance)
            projectile.damage *= 0.8
            projectile.jump_point.append(game.Vec2d(projectile.position))

            min_distance = -1
            chain_target = None
            for target in game.Map.monster_list:
                distance = target.position.get_distance(projectile.position)
                if target in projectile.hitten_list:
                    continue

                if target.alive == False:
                    continue

                if min_distance == -1 or distance < min_distance:
                    chain_target = target
                    min_distance = distance

            if chain_target == None:
                projectile.alive = False
                return

            distance = projectile.position.get_distance(chain_target.position)
            if distance <= projectile.jump_distance:
                projectile.direction = chain_target.position - projectile.position
                projectile.direction = projectile.direction.normalized()
            else:
                projectile.alive = False

class TriggerSkillMultiShot(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.KEYDOWN)

    def condition(self):
        if Event.key_code == pygame.K_q:
            return True

        return False

    def action(self):
        player = game.Map.player
        relative_mouse_position = game.Map.game_map.get_pos_from_mouse()

        if player.attack():
            # Multishot!
            direction = relative_mouse_position - player.position
            direction = direction.normalized()
            angle = direction.get_angle()

            arrowcount = player.level
            if arrowcount > 5: arrowcount = 5

            for i in range(-arrowcount, arrowcount + 1):
                direction.angle = angle - (5 * (i))

                #create the projectile
                p = game.Projectile()
                p.position = player.position
                p.direction = direction.normalized()
                p.damage = player.attack_power / 2
                p.owner = player
                game.Map.projectile_list.append(p)

class TriggerSkillChainLightning(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.KEYDOWN)

    def condition(self):
        if Event.key_code == pygame.K_w:
            return True

        return False

    def action(self):
        player = game.Map.player
        relative_mouse_position = game.Map.game_map.get_pos_from_mouse()

        # Chain Lightning!
        if player.attack():
            direction = relative_mouse_position - player.position

            modifier = (1.0 + (player.level / 2.0))
            if modifier >= 2.0: modifier = 2.0

            p = game.Chain()
            p.position = player.position
            p.direction = direction.normalized()
            p.damage = player.attack_power * modifier
            p.set_range(player.range)
            p.owner = player
            p.jump_point.append(p.position)
            game.Map.projectile_list.append(p)


class TriggerSkillWave(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.KEYDOWN)

    def condition(self):
        if Event.key_code == pygame.K_e:
            return True

        return False

    def action(self):
        player = game.Map.player
        relative_mouse_position = game.Map.game_map.get_pos_from_mouse()

        # Wave
        p = player.shoot(relative_mouse_position)
        if p != None:
            game.Map.projectile_list.append(p)
            modifier = (1.0 + (player.level / 10.0))
            if modifier >= 2.0: modifier = 2.0
            p.damage = p.damage * 3 * modifier
            p.remove_on_hit = False

class TriggerSkillFireball(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.KEYDOWN)

    def condition(self):
        if Event.key_code == pygame.K_r:
            return True

        return False

    def action(self):
        player = game.Map.player
        relative_mouse_position = game.Map.game_map.get_pos_from_mouse()

        #Fireball!
        if player.attack():
            skill = game.Fireball()
            skill.owner = player
            skill.cast(player, relative_mouse_position)

class TriggerSkillHeal(Trigger):
    def __init__(self, event):
        Trigger.__init__(self, event)
        event.register_trigger(self, EventType.KEYDOWN)

    def condition(self):
        if Event.key_code == pygame.K_t:
            return True

        return False

    def action(self):
        player = game.Map.player
        relative_mouse_position = game.Map.game_map.get_pos_from_mouse()

        #Heal
        if player.attack():
            skill = game.Heal()
            skill.cast(player, player)

            for a in game.Map.ally_list:
                skill.cast(player, a)

